package org.example.service.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.stream.Collectors;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.example.mapper.VulnMapper;
import org.example.pojo.*;
import org.example.service.VulnService;
import org.example.utils.Result;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class CveDataFetcher implements VulnService {
    @Autowired
    private VulnMapper vulnMapper;
    @Autowired
    private ThreadPoolTaskScheduler taskScheduler;
    private Map<Integer, Integer> taskProgress = new ConcurrentHashMap<>();
    private Map<Integer, ScheduledFuture<?>> scheduledTasks = new HashMap<>();

    public static void main(String[] args) {
        String timeStr = "2025-03-16T07:09:00.000Z";
        ScanHistoryVO so = new ScanHistoryVO();
        so.setExcuteTime(timeStr);
//        String timeStr = task.getExcuteTime();
        Instant instant = Instant.parse(timeStr);
        // 将 Instant 对象转换为 ZonedDateTime 对象，这里使用系统默认时区
        ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
        // 从 ZonedDateTime 对象中提取 LocalTime
        LocalTime time = zonedDateTime.toLocalTime();
        String format = String.format("0 %d %d * * *", time.getMinute(), time.getHour());
        System.out.println(format);
    }
    public static ZonedDateTime[] getRandomZonedDateTimeRange(ZonedDateTime start, ZonedDateTime end, int maxMonths) {
        Random random = new Random();

        while (true) {
            // 计算起始时间和结束时间之间的秒数
            long secondsBetween = ChronoUnit.SECONDS.between(start, end);

            // 生成两个随机的时间点
            long randomSeconds1 = (long) (random.nextDouble() * secondsBetween);
            long randomSeconds2 = (long) (random.nextDouble() * secondsBetween);

            // 确保第一个时间点早于第二个时间点
            ZonedDateTime randomDateTime1 = start.plusSeconds(Math.min(randomSeconds1, randomSeconds2));
            ZonedDateTime randomDateTime2 = start.plusSeconds(Math.max(randomSeconds1, randomSeconds2));

            // 检查时间跨度是否不超过 maxMonths 个月
            long monthsBetween = ChronoUnit.MONTHS.between(randomDateTime1, randomDateTime2);
            if (monthsBetween <= maxMonths) {
                return new ZonedDateTime[]{randomDateTime1, randomDateTime2};
            }
        }
    }
    // 获取CVE详情
    public static JSONObject getCveDetails(Integer vulnNum) {
        // 定义时间范围的起始和结束时间
        ZonedDateTime start = ZonedDateTime.of(2000, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC);
        ZonedDateTime end = ZonedDateTime.now(ZoneOffset.UTC);

        // 生成随机时间范围，时间跨度不超过 5 个月
        ZonedDateTime[] randomRange = getRandomZonedDateTimeRange(start, end, 5);

        // 格式化输出
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        String urlStr = "https://services.nvd.nist.gov/rest/json/cves/2.0/?pubStartDate="+randomRange[0].format(formatter)+"&pubEndDate="+randomRange[1].format(formatter)+"&resultsPerPage="+ vulnNum;
        try {
            URL url = new URL(urlStr);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");

            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
                reader.close();
                return new JSONObject(response.toString());
            } else {
                System.out.println("Failed to fetch data. Status code: " + responseCode);
                return null;
            }
        } catch (IOException | JSONException e) {
            e.printStackTrace();
            return null;
        }
    }

    // 确定漏洞类型
    public static String determineVulnerabilityType(String key) {
        String[] systemVulnKeywords = {"cpe:2.3:o:", "cpe:2.3:h:"};
        String[] appVulnKeywords = {"cpe:2.3:a:"};
        String[] dbVulnKeywords = {"cpe:2.3:d:"};

        for (String keyword : systemVulnKeywords) {
            if (key.contains(keyword)) {
                return "系统漏洞";
            }
        }
        for (String keyword : appVulnKeywords) {
            if (key.contains(keyword)) {
                return "应用漏洞";
            }
        }
        for (String keyword : dbVulnKeywords) {
            if (key.contains(keyword)) {
                return "数据库漏洞";
            }
        }
        return "未知";
    }
    /**
     * 从描述中提取漏洞类型
     */
    private static String extractVulnerabilityType(String description) {
        if (description.contains("overflow")) {
            return "缓冲区溢出";
        } else if (description.contains("escalation")) {
            return "权限提升";
        } else if (description.contains("SQL injection")) {
            return "SQL注入";
        } else if (description.contains("cross-site")) {
            return "跨站脚本";
        } else if (description.contains("service")) {
            return "拒绝服务";
        } else if (description.contains("information")) {
            return "信息泄露";
        } else {
            return "";
        }
    }
    @Override
    public PageBean scanList(Integer page, Integer size, String taskName, String start,String end, String status) {
        PageHelper.startPage(page, size);
        List<VulnScanTask> vulnScanTasks = vulnMapper.scanList(taskName, start, end, status);
        Page<VulnScanTask> p = (Page<VulnScanTask>) vulnScanTasks;
        PageBean pageBean = new PageBean(p.getTotal(), p.getResult());
        return pageBean;
    }

    public void getCveData(Integer scanHisId, Integer vulnNum) {
        JSONObject cveData = getCveDetails(vulnNum);
        if (cveData != null) {
            Vulnerabilities vuln = new Vulnerabilities();
            try {
                JSONArray vulnerabilities = cveData.getJSONArray("vulnerabilities");
                for (int i = 0; i < vulnerabilities.length(); i++) {
                    JSONObject vulnerability = vulnerabilities.getJSONObject(i).getJSONObject("cve");
                    vuln.setScanHistoryId(scanHisId);
                    // cve_id
                    String cveId = vulnerability.getString("id");
                    vuln.setCveId(cveId);
                    // 获取描述信息（通常取英文描述）
                    String description = "";
                    String suggestion = "";
                    JSONArray descriptions = vulnerability.getJSONArray("descriptions");
                    for (int j = 0; j < descriptions.length(); j++) {
                        JSONObject desc = descriptions.getJSONObject(j);
                        if ("en".equals(desc.getString("lang"))) {
                            description = desc.getString("value");
                        }
                        if ("es".equals(desc.getString("lang"))) {
                            suggestion = desc.getString("value");
                        }
                    }
                    vuln.setDescription(description);
                    vuln.setSuggestion(suggestion);
                    // 获取漏洞名称
                    String vulnName = extractVulnerabilityType(description) + " " + cveId;
                    vuln.setVulnName(vulnName);
                    java.util.Date currentDate1 = new Date();
                    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String formattedDate1 = sdf1.format(currentDate1);
                    // 发布时间
                    String publishTime = formattedDate1;//vulnerability.getString("published");
                    vuln.setPublishTime(publishTime);
                    // 获取CVSS指标
                    JSONObject cvssMetric = null;
                    for (int k = 41; k > 1; k--) {
                        String key = "cvssMetricV" + k;
                        if (vulnerability.getJSONObject("metrics").has(key)) {
                            cvssMetric = vulnerability.getJSONObject("metrics").getJSONArray(key).getJSONObject(0);
                            break;
                        }
                    }

                    // 影响产品（这里只提取第一个配置）
                    JSONArray affectedProducts = new JSONArray();
                    if (vulnerability.has("configurations")) {
                        JSONArray configurations = vulnerability.getJSONArray("configurations");
                        for (int l = 0; l < configurations.length(); l++) {
                            JSONObject node = configurations.getJSONObject(l);
                            JSONArray nodes = node.getJSONArray("nodes");
                            for (int m = 0; m < nodes.length(); m++) {
                                JSONObject cpeMatch = nodes.getJSONObject(m);
                                affectedProducts.put(cpeMatch.getJSONArray("cpeMatch"));
                            }
                        }
                    }

                    String affectedProduct = "N/A";
                    String vulnType = "";
                    if (affectedProducts.length() > 0 && affectedProducts.getJSONArray(0).length() > 0) {
                        JSONObject firstProduct = affectedProducts.getJSONArray(0).getJSONObject(0);
                        affectedProduct = firstProduct.getString("matchCriteriaId");
                        vulnType = determineVulnerabilityType(firstProduct.getString("criteria"));
                    }
                    vuln.setAffectedProduct(affectedProduct);
                    vuln.setVulnType(vulnType);
                    // 弱点
                    String weakness = "N/A";
                    if (vulnerability.has("weaknesses")) {
                        JSONArray weaknesses = vulnerability.getJSONArray("weaknesses");
                        if (weaknesses.length() > 0) {
                            JSONObject firstWeakness = weaknesses.getJSONObject(0);
                            JSONArray descriptionsArray = firstWeakness.getJSONArray("description");
                            if (descriptionsArray.length() > 0) {
                                weakness = descriptionsArray.getJSONObject(0).getString("value");
                            }
                        }
                    }
                    vuln.setWeakness(weakness);
                    // 严重等级和评分
                    String severity = "INFO";
                    String baseScore = "0.0";
                    if (cvssMetric != null && cvssMetric.has("baseSeverity")) {
                        severity = cvssMetric.getString("baseSeverity");
                    }
                    if (cvssMetric != null && cvssMetric.has("cvssData")) {
                        baseScore = String.valueOf(cvssMetric.getJSONObject("cvssData").getDouble("baseScore"));
                        if (cvssMetric.getJSONObject("cvssData").has("baseSeverity")) {
                            severity = cvssMetric.getJSONObject("cvssData").getString("baseSeverity");
                        }

                    }
                    vuln.setSeverity(severity);
                    vuln.setBaseScore(baseScore);
                    // 参考文献
                    String reference = "N/A";
                    if (vulnerability.has("references") && vulnerability.getJSONArray("references").length() > 0) {
                        reference = vulnerability.getJSONArray("references").getJSONObject(0).getString("url");
                    }
                    vuln.setReference(reference);
                    vuln.setOrigin("任务扫描");
                    vulnMapper.insert(vuln);
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public PageBean vulnList(Integer page, Integer size, Integer tempId, Integer scanId, String cveId, String vulnName, String vulnType, String severity, String status, String start, String end, String keywords) {
        PageHelper.startPage(page, size);
        List<Vulnerabilities> vulnerabilities = vulnMapper.vulnList(tempId, scanId, cveId, vulnName, vulnType, severity, status, start, end, keywords);
        Page<Vulnerabilities> p = (Page<Vulnerabilities>) vulnerabilities;
        PageBean pageBean = new PageBean(p.getTotal(), p.getResult());
        return pageBean;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result scanTaskScheduler(Integer taskId) {
        try {
            VulnScanTask task = vulnMapper.getScanTaskById(taskId);
            ScanHistoryVO scanHistoryVO = new ScanHistoryVO();
            scanHistoryVO.setTaskId(taskId);
            scanHistoryVO.setTaskName(task.getTaskName());
            scanHistoryVO.setExcuteWay(task.getExcuteWay());
            scanHistoryVO.setExcuteTime(task.getExcuteTime());
            scanHistoryVO.setDayOfWeek(task.getDayOfWeek());
            vulnMapper.startScan(scanHistoryVO);
            if (Objects.equals(task.getExcuteWay(), "单次")) {
                ZonedDateTime now = ZonedDateTime.now(ZoneId.systemDefault());
                ScheduledFuture<?> future = taskScheduler.schedule(() -> executeTask(scanHistoryVO), now.toInstant());
                scheduledTasks.put(task.getId(), future);
            } else {
                String cronExpression = generateCronExpression(scanHistoryVO);
                ScheduledFuture<?> future = taskScheduler.schedule(() -> executeTask(scanHistoryVO), new CronTrigger(cronExpression));
                scheduledTasks.put(task.getId(), future);
            }
            return Result.success();
        }catch (Exception e) {
            return Result.error();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result cancelScanTask(ScanHistoryVO task) {
        Integer taskId = task.getId();
        ScheduledFuture<?> future = scheduledTasks.get(taskId);
        if (future != null) {
            future.cancel(true);
            scheduledTasks.remove(taskId);
            task.setStatus("已停止");
            vulnMapper.updatevulnscanhistory(task);
            return Result.success();
        } else {
            return Result.error();
        }
    }

    @Override
    public Integer scanProgress(Integer id) {
        return taskProgress.get(id);
    }

    @Override
    public List<VulnerabilityCount> getVulnerabilityCountLast7Days() {
        // 获取过去7天的日期列表
        List<String> last7Days = generateLast7Days();

        // 查询数据库中的数据
        List<VulnerabilityCount> dbData = vulnMapper.countVulnerabilitiesByDate();

        // 将查询结果转换为Map，方便快速查找
        Map<String, Integer> dataMap = dbData.stream()
                .collect(Collectors.toMap(
                        item -> item.getDate().toString(), // 假设日期格式为yyyy-MM-dd
                        VulnerabilityCount::getCount
                ));

        // 构建最终结果，确保过去7天每天都有数据
        List<VulnerabilityCount> result = new ArrayList<>();
        for (String day : last7Days) {
            VulnerabilityCount count = new VulnerabilityCount();
            count.setDate(day);
            count.setCount(dataMap.getOrDefault(day, 0)); // 如果数据库中没有数据，则设置为0
            result.add(count);
        }
        return result;
    }
    // 生成过去7天的日期列表
    private List<String> generateLast7Days() {
        List<String> days = new ArrayList<>();
        LocalDate today = LocalDate.now();
        for (int i = 6; i >= 0; i--) {
            LocalDate date = today.minusDays(i);
            days.add(date.toString()); // 格式为yyyy-MM-dd
        }
        return days;
    }
    private String generateCronExpression(ScanHistoryVO task) {
        String timeStr = task.getExcuteTime();
        Instant instant = Instant.parse(timeStr);
        // 将 Instant 对象转换为 ZonedDateTime 对象，这里使用系统默认时区
        ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
        // 从 ZonedDateTime 对象中提取 LocalTime
        LocalTime time = zonedDateTime.toLocalTime();
        if (Objects.equals(task.getExcuteWay(), "每天")) {
            return String.format("0 %d %d * * *", time.getMinute(), time.getHour());
        } else {
            DayOfWeek dayOfWeek = task.getDayOfWeek();
            return String.format("0 %d %d ? * %d", time.getMinute(), time.getHour(), dayOfWeek.getValue());
        }
    }

    private void executeTask(ScanHistoryVO task) {
        task.setStartTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        task.setStatus("扫描中");
        vulnMapper.updatevulnscanhistory(task);
        Integer taskId = task.getId();
        int scanTime = (int) (Math.random() * 30 + 10); //随机10-30秒
        for (int i = 0; i <= 100; i++) {
            // 检查线程是否被中断
            if (Thread.currentThread().isInterrupted()) {
                return; // 退出任务逻辑
            }
            taskProgress.put(taskId, i);
            try {
                Thread.sleep((scanTime * 1000L) / 100); // 进度更新
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt(); // 重新设置中断状态
                return;
            }
        }
        int vulnNum = (int) (Math.random() * 25 + 5);
        getCveData(taskId, vulnNum);
        task.setStatus("扫描完成");
        task.setEndTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        vulnMapper.updatevulnscanhistory(task);
        if (Objects.equals(task.getExcuteWay(), "单次")) {
            ScheduledFuture<?> future = scheduledTasks.get(taskId);
            if (future != null) {
                future.cancel(true);
                scheduledTasks.remove(taskId);
            }
        }
    }
}